package main

import (
	"flag"
	"fmt"
	"os"
	"path"
	"runtime"
	"strings"
)

// 用于打印的颜色
var cr0 = "\033[0m"     // 结束
var cr1 = "\033[01;37m" // 白色
var cr2 = "\033[01;32m" // 绿色
var cr3 = "\033[01;33m" // 黄色
var cr4 = "\033[01;31m" // 红色

func main() {

	// windows下不打印颜色
	if runtime.GOOS == "windows" {
		cr0 = ""
		cr1 = ""
		cr2 = ""
		cr3 = ""
		cr4 = ""
	}

	// 获取appName
	flag.Parse()
	args := flag.Args()
	if len(args) < 1 {
		fmt.Println(cr1, "Usage：gnew [appName] [path]", cr0)
		fmt.Println(cr2, "Example: gnew test", cr0)
		fmt.Println(cr2, "Example: gnew test D:\\", cr0)
		fmt.Println(cr2, "Example: gnew test /home/tom/", cr0)
		return
	}
	appName := args[0]
	fmt.Println("Start to create project", cr3, appName, cr0, "...")

	// 获取路径
	appPath := "."
	if len(args) >= 2 {
		appPath = args[1]
	}

	// 创建目录
	appPath = path.Join(appPath, appName)
	if IsExist(appPath) {
		fmt.Println(cr4, "The project already exists!", cr0)
		return
	}
	_ = os.MkdirAll(appPath, 0755)
	_ = os.MkdirAll(path.Join(appPath, "routers"), 0755)
	_ = os.MkdirAll(path.Join(appPath, "controllers"), 0755)

	// 创建各个文件
	Check(Write2File(path.Join(appPath, "go.mod"), strings.Replace(strMod, "{{.AppName}}", appName, -1)))
	Check(Write2File(path.Join(appPath, ".gitignore"), strings.Replace(strGitignore, "{{.AppName}}", appName, -1)))
	Check(Write2File(path.Join(appPath, "main.go"), strings.Replace(strMain, "{{.AppName}}", appName, -1)))
	Check(Write2File(path.Join(appPath, "routers", "router.go"), strings.Replace(strRouter, "{{.AppName}}", appName, -1)))
	Check(Write2File(path.Join(appPath, "controllers", "controller.go"), strings.Replace(strController, "{{.AppName}}", appName, -1)))
	Check(Write2File(path.Join(appPath, "controllers", "example.go"), strings.Replace(strExample, "{{.AppName}}", appName, -1)))

	fmt.Println("Project", cr3, appName, cr0, cr2, "has been created successfully!", cr0)
}

func Check(err error) {
	if err != nil {
		fmt.Println(cr3, "Operation failed:", cr0, cr4, err, cr0)
		panic(err)
	}
}

func Write2File(filename, content string) (err error) {
	f, err := os.Create(filename)
	if err != nil {
		return
	}
	defer func(f *os.File) {
		_ = f.Close()
	}(f)
	_, err = f.WriteString(content)
	fmt.Println(cr3, "-- Created: ", cr0, cr2, filename, cr0)
	return
}

func IsExist(path string) bool {
	_, err := os.Stat(path)
	return err == nil || os.IsExist(err)
}

var strMain = `package main

import (
	_ "{{.AppName}}/controllers" // 这个是为了保证这个包被引入，否则不会调用init函数
	"{{.AppName}}/routers"
	"github.com/15125505/zlog/log"
	"github.com/gorilla/mux"
	"github.com/urfave/negroni"
	"net/http"
)

func init() {
	log.Log.SetLogFile("logs/{{.AppName}}")
	log.Log.SetFileColor(true)
	log.Log.SetAdditionalErrorFile(true)
	log.Log.SetLogLevel(log.LevelDebug)
}

func main() {
	r := mux.NewRouter()
	routers.CreateHandle(r)
	n := negroni.New(negroni.NewRecovery(), negroni.NewStatic(http.Dir("public")), negroni.HandlerFunc(routers.PreProcess))
	n.UseHandler(r)
	n.Run(":5000")
}
`

var strRouter = `package routers

import (
	"fmt"
	"github.com/15125505/zlog/log"
	"github.com/gorilla/mux"
	"github.com/urfave/negroni"
	"net/http"
	"sort"
	"strings"
	"time"
)

// SubController 子路由必须实现的接口
type SubController interface {
	Handle(m *mux.Router, tpl string)
}

// 用于存储用户参数的结构
type handle struct {
	sub SubController
	tpl string
}

// 用户路由信息表
var handles []handle

// AddController 添加控制器
func AddController(sub SubController, tpl string) {
	handles = append(handles, handle{sub, tpl})
}

// CreateHandle 设置路由
func CreateHandle(m *mux.Router) {
	for _, v := range handles {
		v.sub.Handle(m, v.tpl)
	}
}

// PreProcess 预处理（解析参数和日期打印）
func PreProcess(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {

	// 在页面被处理之前，你可以做一些工作
	start := time.Now()

	// 加入跨域的处理（如果需要进行跨域兼容，那么取消下面这句话的注释即可）
	// rw.Header().Add("Access-Control-Allow-Origin", "*")

	// 这句代码放在next之前，可以避免每次获取参数之前都进行ParseForm操作
	_ = r.ParseForm()

	// 继续后续的处理
	next(rw, r)

	// 为了避免阿里云的SLB日志过多，不打印HEAD请求，
	if r.Method == "HEAD" {
		return
	}

	// 获取http状态码
	res := rw.(negroni.ResponseWriter)
	code := res.Status()
	var color string
	switch {
	case code >= 200 && code < 300:
		color = "\033[01;42;34m" // 绿色
	case code >= 300 && code < 400:
		color = "\033[01;47;34m" // 白色
	case code >= 400 && code < 500:
		color = "\033[01;43;34m" // 黄色
	default:
		color = "\033[01;41;33m" // 红色
	}

	// 获取参数信息
	var keys []string
	for k := range r.Form {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	var param string
	for _, k := range keys {
		param += " " + k + ":"
		param += fmt.Sprint(r.Form[k])
	}
	param = strings.TrimLeft(param, " ")

	// 获取请求者IP地址
	ip := "127.0.0.1"
	if forwards := r.Header.Get("X-Forwarded-For"); forwards != "" {
		ips := strings.Split(forwards, ",")
		if len(ips) > 0 {
			ip = ips[0]
		}
	} else {
		ip = r.RemoteAddr
	}
	ips := strings.Split(ip, ":")
	if len(ips) > 0 {
		ip = ips[0]
	}

	// 显示请求详情
	tmpUA := []byte(r.UserAgent())
	if len(tmpUA) > 40 {
		tmpUA = tmpUA[:40]
	}

	log.Info(fmt.Sprintf("%v %v \033[0m\033[37m|%12v\033[32m|%15s\033[01;37m|%5v\033[0m\033[33m|%40v\033[32m|%v\033[33m|%v",
		color,
		res.Status(),
		time.Since(start),
		ip,
		r.Method,
		string(tmpUA),
		r.URL.Path,
		param,
	))
}
`

var strController = `package controllers

import (
	"github.com/unrolled/render"
)

// Render 定义一个用于json或者xml等各种渲染的公共渲染模块
var Render *render.Render = render.New()

// RetInfo 下面是一个常见的一种接口反馈模板
type RetInfo struct {
	Code int         ` + "`" + `json:"code"` + "`" + `
	Err  interface{} ` + "`" + `json:"err,omitempty"` + "`" + `
	Msg  interface{} ` + "`" + `json:"msg,omitempty"` + "`" + `
}
`

var strExample = `package controllers

import (
	"{{.AppName}}/routers"
	"github.com/gorilla/mux"
	"net/http"
)

// ExampleController todo: 定义一个自己的Controller类
type ExampleController struct {
}

// 初始化
func init() {
	// todo: 此处需要设置子路由的前缀
	routers.AddController(&ExampleController{}, "/example")
}

// Handle todo: 为自己的类添加一个这样的Handle函数，注意名称不能随意改动
func (c *ExampleController) Handle(m *mux.Router, tpl string) {

	sub := m.PathPrefix(tpl).Subrouter()

	// todo: 本函数演示了使用绝对路由的方式
	m.HandleFunc("/path1", absolutePath)

	// todo: 本函数演示了使用相对路由的方式
	sub.HandleFunc("/path2", relativePath).Methods("POST")
}

// todo: 访问 http://localhost:5000/path1 将触发本函数的反馈
func absolutePath(w http.ResponseWriter, r *http.Request) {

	// todo:本函数演示了json的输出方法
	ret := RetInfo{}
	ret.Code = 0
	ret.Msg = "OK..."
	_ = Render.JSON(w, http.StatusOK, ret)
	return
}

// todo: 使用POST方式访问 http://localhost:5000/example/path2 将触发本函数的反馈
func relativePath(w http.ResponseWriter, r *http.Request) {
	_, _ = w.Write([]byte("This is a relative path."))
	return
}
`

var strGitignore = `.idea
*.exe
*.log`

var strMod = `
module {{.AppName}}

go 1.17

require (
	github.com/15125505/zlog v0.0.0-20170717104426-98bfa8f1ca3f
	github.com/gorilla/mux v1.8.0
	github.com/unrolled/render v1.4.1
	github.com/urfave/negroni v1.0.0
)

require (
	github.com/fsnotify/fsnotify v1.4.9 // indirect
	golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea // indirect
)
`
